import json
import os
from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout, QLabel, QStyledItemDelegate, QGridLayout, QPushButton, QFileDialog, QComboBox, QFrame, QGroupBox, QAbstractItemView, QTableWidgetItem, QTableWidget, QSpacerItem, QSizePolicy
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QObject, QEvent
from PyQt5.QtGui import QPainter, QPen, QColor, QDoubleValidator
from pydantic import BaseModel, Field, field_validator
import gui.controls.envs as envs

from ..basic_grid_component import BasicGridComponent
# import gui.controls.envs as envs
from ..dlg_model_params import DlgModelParams
from ..c_params_table import CParamsTable
from ..dlg_params import DlgParams
from ..utils import Utils
from ..custom_widget import LineEditFloat, LineEditStr, LineEditInt, SelectBox


from enum import Enum

class EventFilter(QObject):
    def eventFilter(self, event):
        # 检查事件是否为按键事件，并且按键是否为回车键
        if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Return:
            # 返回 True 表示事件已被处理，不再传播
            return True
        # 返回 False 表示事件未被处理，可以继续传播
        return False

class FloatValidatorDelegate(QStyledItemDelegate):
    def __init__(self, parent=None):
        super(FloatValidatorDelegate, self).__init__(parent)
        self.validator = QDoubleValidator(self)

    def createEditor(self, parent, option, index):
        editor = super(FloatValidatorDelegate, self).createEditor(parent, option, index)
        editor.setValidator(self.validator)
        return editor

class FineLine(QFrame):
    def __init__(self, parent=None):
        super(FineLine, self).__init__(parent)
        self.setFrameShape(QFrame.HLine)
        self.setFrameShadow(QFrame.Sunken)

    def paintEvent(self, event):
        painter = QPainter(self)
        color = QColor(255, 255, 255, 127)  # 透明度范围0-255，127为半透明

        painter.setPen(QPen(color, 0.5))  # 设置画笔宽度为0.5像素
        painter.drawLine(0, self.height() / 2, self.width(), self.height() / 2)

class CustomTable(CParamsTable):
    
    def __init__(self, rows=0, columns=2, parent=None):
        super(CustomTable, self).__init__(rows=rows, columns=columns, parent=parent)
        self.setEditTriggers(QAbstractItemView.DoubleClicked)
        self.verticalHeader().setVisible(True)


class StrEnumWithAlias(str, Enum):
    def __new__(cls, value, alias):
        obj = str.__new__(cls, value)
        obj._value_ = value
        obj.alias = alias
        return obj
    
class DamageTypeEnum(StrEnumWithAlias):
    t1 = "复材表面损伤", "复材表面损伤"
    t2 = "蜂窝芯子损伤", "蜂窝芯子损伤"
    t3 = "贯穿式损伤", "贯穿式损伤"
    
class layerTypeEnum(StrEnumWithAlias):
    t1 = "原铺层", "原铺层"
    t2 = "加强层", "加强层"

# 定义 Pydantic 模型
class ParamsModelingData(BaseModel):
    presetModelPath: str = Field(
        description="预设模型路径",
        alias="presetModelPath",
        required=True,
        extra={"button": [{"name":'importPresetModel',"title":"浏览...","width":70}],"hide":True}
    )
    damageCount: int = Field(
        description="损伤数量",
        alias="damageCount",
        required=True,
    )
    damageType: DamageTypeEnum = Field(
        description="损伤类型",
        alias="damageType",
        required=True,
    )
    damageCenterX: float = Field(
        description="损伤中心位置x",
        alias="damageCenterX",
        required=True,
    )
    damageCenterY: float = Field(
        description="损伤中心位置y",
        alias="damageCenterY",
        required=True,
    )
    honeycombCoreHeight: float = Field(
        description="蜂窝芯子高度",
        alias="honeycombCoreHeight",
        required=True,
    )
    damageExcisionDiameter: float = Field(
        description="损伤挖除直径",
        alias="damageExcisionDiameter",
        required=True,
    )
    submodelSizeL: float = Field(
        description="子模型尺寸长度",
        alias="submodelSizeL",
        required=True,
    )
    submodelSizeW: float = Field(
        description="子模型尺寸宽度",
        alias="submodelSizeW",
        required=True,
    )
    submodelSize: float = Field(
        description="子模型尺寸（长*宽）",
        alias="submodelSize",
        required=True,
        extra={"disable": True}
    )
    lapWidth: float = Field(
        description="搭接宽度",
        alias="lapWidth",
        required=True,
    )
    adhesiveLayerThickness: float = Field(
        description="胶层厚度",
        alias="adhesiveLayerThickness",
        required=True,
    )
    additionalExcisionSize: float = Field(
        description="额外挖除尺寸",
        alias="additionalExcisionSize",
        required=True,
    )
    lowerAdhesiveLayersCount: float = Field(
        description="下铺层修补胶层层数",
        alias="lowerAdhesiveLayersCount",
        required=True,
    )
    # 
    topPlyNumber: int = Field(
        description="上铺层层数",
        alias="topPlyNumber",
        required=True,
        extra={"hide":True}
    )
    bottomPlyNumber: int = Field(
        description="下铺层层数",
        alias="bottomPlyNumber",
        required=True,
        extra={"hide":True}
    )
    topRepairPlyNumber: int = Field(
        description="上铺层修补层层数",
        alias="topRepairPlyNumber",
        required=True,
        extra={"hide":True}
    )
    bottomRepairPlyNumber: int = Field(
        description="下铺层修补层层数",
        alias="bottomRepairPlyNumber",
        required=True,
        extra={"hide":True}
    )
    
    
class ParamsModelingComponent(BasicGridComponent):
    detailParams_modelShow_signal = pyqtSignal(object)
    def __init__(self, parent=None):
        super().__init__(parent)
        
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setSpacing(20)
        self.gridLayout.setVerticalSpacing(20)
        self.gridWidgets['submodelSizeL'].editingFinished.connect(lambda:self.on_submodelSize_changed())
        self.gridWidgets['submodelSizeW'].editingFinished.connect(lambda:self.on_submodelSize_changed())
        # self.gridWidgets['topRepairPlyNumber'].editingFinished.connect(lambda:self.on_topRepairPlyNumber_changed())
        # self.gridWidgets['damageType'].currentIndexChanged.connect(self.on_damageType_changed)
        # self.setDefault()
        
    def get_model(self):
        return ParamsModelingData
    
    def on_import_clicked(self):
        # 创建QFileDialog对话框实例
        file_dialog = QFileDialog()
        file_dialog.setNameFilter("CAE files (*.cae)")

        # 设置对话框标题
        file_dialog.setWindowTitle("选择预设模型文件")
        # 显示对话框并获取用户选择的文件
        if file_dialog.exec_():
            # 用户点击了“打开”或“保存”，获取选中的文件路径
            file_path = file_dialog.selectedFiles()[0]
            self.gridWidgets['presetModelPath'].setText(file_path)
       
    def setDefault(self):
        self.gridWidgets['damageCount'].setText("1")
    
    def on_submodelSize_changed(self):
        if self.gridWidgets['submodelSizeL'].text() and self.gridWidgets['submodelSizeW'].text():
            try:
                submodelSizeL = float(self.gridWidgets['submodelSizeL'].text())
                submodelSizeW = float(self.gridWidgets['submodelSizeW'].text())
                self.gridWidgets['submodelSize'].setText(str(submodelSizeL*submodelSizeW))
            except ValueError:
                self.gridWidgets['submodelSize'].setText("")
        else:
            self.gridWidgets['submodelSize'].setText("")
            
    def on_topRepairPlyNumber_changed(self):
        if self.gridWidgets['topPlyNumber'].text() and self.gridWidgets['topRepairPlyNumber'].text():
            try:
                topPlyNumber = float(self.gridWidgets['topPlyNumber'].text())
                topRepairPlyNumber = float(self.gridWidgets['topRepairPlyNumber'].text())
                if topRepairPlyNumber < topPlyNumber:
                    envs.MBox.warning("上铺层修补件层数不可小于上铺层层数！")
                    self.gridWidgets['topRepairPlyNumber'].setText("")
                    return
            except ValueError:
                pass
       
            
    def on_damageType_changed(self):
        if (self.gridWidgets['damageType'].currentText() == "贯穿式损伤"):
            self.gridWidgets['additionalExcisionSize'].setDisabled(False)
            self.gridWidgets['lowerAdhesiveLayersCount'].setDisabled(False)
            self.gridWidgets['bottomRepairPlyNumber'].setDisabled(False)
            self.gridWidgets['bottomRepairPlyTable'].setDisabled(False)
        else:
            self.gridWidgets['additionalExcisionSize'].setDisabled(True)
            self.gridWidgets['lowerAdhesiveLayersCount'].setDisabled(True)
            self.gridWidgets['bottomRepairPlyNumber'].setDisabled(True)
            self.gridWidgets['bottomRepairPlyTable'].setDisabled(True)
      
    def updateUi(self,data):
        for k, v in data.items():
            if isinstance(self.gridWidgets[k], QTableWidget):
                self.loadTableData(self.gridWidgets[k],v)
            elif isinstance(self.gridWidgets[k], QComboBox):
                self.gridWidgets[k].setCurrentText(v)
            else:
                self.gridWidgets[k].setValue(v)
                # self.gridWidgets[k].setText(str(v))
                
    def loadTableData(self, table, data):
        for index, item in enumerate(data):
            if "铺层名称" in item:
                table.addRow([item["铺层名称"], item["厚度"]])
            else:
                table.addRow([item["厚度"]])
        table.set_columns_editable()
        
        
    def updateUI(self):
        fileds = self.model.model_fields
        flagRow = 0
        for row, (field_name, field_info) in enumerate(fileds.items()):
            
            buttonList = []
            disable = False
            hide = False
            if field_info.json_schema_extra.get('extra') and field_info.json_schema_extra.get('extra').get('hide'):
                hide = field_info.json_schema_extra.get('extra').get('hide')
            if field_info.json_schema_extra.get('extra') and field_info.json_schema_extra.get('extra').get('button'):
                buttonList = field_info.json_schema_extra.get('extra').get('button')
            if field_info.json_schema_extra.get('extra') and field_info.json_schema_extra.get('extra').get('disable'):
                disable = field_info.json_schema_extra.get('extra').get('disable')
            # print("hide=",hide)
            if hide:
                flagRow = flagRow + 1
                continue
            row = row - flagRow
            label = QLabel(field_info.description)
            self.gridLayout.addWidget(label, 2*row, 0)
            if issubclass(field_info.annotation, Enum):
                self.gridWidgets[field_name] = SelectBox(options=[{"title":item.alias, "value":item.alias} for item in list(field_info.annotation)])
                self.gridWidgets[field_name].setMinimumWidth(100)
                # self.gridWidgets[field_name].setPlaceholderText(f"请选择{field_info.description}")
                # self.gridWidgets[field_name] = QComboBox()
                # self.gridWidgets[field_name].setMinimumWidth(100)
                # self.gridWidgets[field_name].addItems(
                #     [item.alias for item in list(field_info.annotation)]
                # )
            else:
                field_type = field_info.annotation
                if field_type is float :
                    widget = LineEditFloat()
                elif field_type is int :
                    widget = LineEditInt()
                else:
                    widget = LineEditStr()
                if not disable:
                    widget.setPlaceholderText(f"请输入{field_info.description}")
                self.gridWidgets[field_name] = widget
            if disable:
                self.gridWidgets[field_name].setEnabled(False)
            self.gridLayout.addWidget(self.gridWidgets[field_name], 2*row, 1)
            if row != 0:
                spacer = QSpacerItem(20, 100, QSizePolicy.Minimum, QSizePolicy.Expanding)
                self.gridLayout.addItem(spacer, 2*row-1, 0, 1)  # 在第1行添加一个占位符，占据第0列和第1列
            for i, btnItem in enumerate(buttonList):
                self.gridWidgets[btnItem.get("name")] = QPushButton(btnItem.get("title"))
                self.gridWidgets[btnItem.get("name")].setMinimumWidth(btnItem.get("width"))
                self.gridWidgets[btnItem.get("name")].setFixedHeight(30)
                self.gridLayout.addWidget(self.gridWidgets[btnItem.get("name")], 2*row, 2+i)
            # self.gridLayout.setColumnStretch(1, 1)
        self.setLayout(self.gridLayout)

class DlgParamsModeling(DlgModelParams):
    paramsModeling_save_signal = pyqtSignal(object)
    def __init__(self, parent=None):
        super(DlgParamsModeling, self).__init__(parent)
        
        self.setWindowTitle('参数化建模')
        # self.labelWidth = SystemFont.getSystemFontWidth("下铺层修补层层数-")
        self.pBtnOK.setFocusPolicy(Qt.NoFocus)
        self.pBtnCancel.setFocusPolicy(Qt.NoFocus)
        # self.setMinimumWidth(800)
        self.resize(800, 700)
        layout = QVBoxLayout()
        componentLayout = QHBoxLayout()
        
        self.component = ParamsModelingComponent()
        vLayout = QVBoxLayout()
        vLayout.addWidget(self.component)
        vLayout.addStretch(1)
        
        # componentLayout.addWidget(self.component)
        componentLayout.addLayout(vLayout)
        
        topLayout = QHBoxLayout()
        label = QLabel("预设模型路径")
        self.component.gridWidgets['presetModelPath'] = LineEditStr()
        self.component.gridWidgets['presetModelPath'].setPlaceholderText("请选择预设模型路径")
        self.component.gridWidgets["importPresetModel"] = QPushButton('浏览...')
        self.component.gridWidgets["importPresetModel"].setFocusPolicy(Qt.NoFocus)
        topLayout.addWidget(label)
        topLayout.addWidget(self.component.gridWidgets['presetModelPath'])
        topLayout.addWidget(self.component.gridWidgets['importPresetModel'])
        topLayout.setContentsMargins(-1, -1, -1, 15)
        
        # componentLayout.addLayout(topLayout)
        
        
        
        self.component.gridWidgets['importPresetModel'].clicked.connect(self.component.on_import_clicked)
        
        topPlyLayers = self.createLayersTable("上铺层", "topPly")
        topRepairPlyLayers = self.createLayersTable("上铺层修补层", "topRepairPly", 1)
        bottomPlyLayers = self.createLayersTable("下铺层", "bottomPly")
        bottomRepairPlyLayers = self.createLayersTable("下铺层修补层", "bottomRepairPly", 1)
        
        
        # self.component.gridWidgets['topPlyNumber'].keyPressEvent = self.line_edit_event_filter
        # self.component.gridWidgets['bottomPlyNumber'].keyPressEvent = self.line_edit_event_filter
        # self.component.gridWidgets['topRepairPlyNumber'].keyPressEvent = self.line_edit_event_filter
        # self.component.gridWidgets['bottomRepairPlyNumber'].keyPressEvent = self.line_edit_event_filter
        # self.component.gridWidgets['topPlyNumber'].installEventFilter(self, self.line_edit_event_filter)
        # self.component.gridWidgets['topPlyNumber'].installEventFilter(EventFilter())
        
        
        self.component.gridWidgets['topRepairPlyNumber'].editingFinished.connect(lambda:self.component.on_topRepairPlyNumber_changed())
        
        vLayout = QVBoxLayout()
        vLayout.addLayout(topPlyLayers)
        vLayout.addLayout(bottomPlyLayers)
        vLayout.addLayout(topRepairPlyLayers)
        vLayout.addLayout(bottomRepairPlyLayers)
        # gridLayout = QGridLayout()
        # gridLayout.addLayout(vLayout, 0, 0)
        
        componentLayout.addItem(QSpacerItem(10, 20, QSizePolicy.Minimum, QSizePolicy.Expanding))
        componentLayout.addLayout(vLayout)
        
        layout.addLayout(topLayout)
        # layout.addItem(QSpacerItem(10, 40, QSizePolicy.Minimum, QSizePolicy.Expanding))
        layout.addLayout(componentLayout)
        
        self.vbox.insertLayout(0, layout)
        
        self.component.gridWidgets['damageType'].currentIndexChanged.connect(self.component.on_damageType_changed)
        self.component.setDefault()
        
        
        jsonPath = "data/ipimas/uis/ParamsModeling.json"
        if not os.path.exists(jsonPath):
            data = Utils.read_json("data/ipimas/uis/ParamsModeling.json")
        else:
            data = Utils.read_json(jsonPath)
        self.updateUi(data)
        self.component.on_damageType_changed()
        # self.component.gridWidgets['topPlyNumber'].keyPressEvent = self.onKeyPress
        # self.component.gridWidgets['topPlyNumber'].returnPressed.connect(self.onLineEditReturnPressed)

        
    def onKeyPress(self, event):
        if event.key() in (Qt.Key_Return, Qt.Key_Enter):
            # 忽略回车键的默认行为
            event.ignore()
            # 将焦点移至表格的第一项
            # self.table_widget.setFocus()
            # self.table_widget.setCurrentCell(0, 0)  # 设置焦点到表格的第一项
        else:
            # 允许其他按键的默认行为
            event.accept()
            
    def onLineEditReturnPressed(self):
        pass
        # 当回车键被按下时，将焦点移至表格的第一项
        # self.table_widget.setFocus()
        # self.table_widget.setCurrentCell(0, 0)  # 设置焦点到表格的第一项
        
    def createLayersTable(self, title, name, type=0):
        numLayout = QHBoxLayout()
        label = QLabel(f"{title}层数")
        label.setFixedWidth(160)
        edit = LineEditInt()
        edit.setPlaceholderText("请输入层数")
        edit.editingFinished.connect(lambda:self.on_text_changed(flag=name))
        # edit.focusOutEvent =  partial(self.on_focus_out, name) 
        self.component.gridWidgets[f'{name}Number'] = edit
        numLayout.addWidget(label)
        numLayout.addWidget(edit)
        numLayout.addStretch(1)
        layersLayout = QVBoxLayout()
        if type == 1:
            table = CustomTable(rows=0, columns=2)
            table.setItemDelegateForColumn(1, FloatValidatorDelegate(table))
            table.setHorizontalHeaderLabels(["铺层名称", "厚度"])
        else:
            table = CustomTable(rows=0, columns=1)
            table.setItemDelegateForColumn(0, FloatValidatorDelegate(table))
            table.setHorizontalHeaderLabels(["厚度"])
        # table.setMaximumHeight(120)
        # table.resize(-1, 120)
        self.component.gridWidgets[f'{name}Table'] = table
        layersLayout.addLayout(numLayout)
        layersLayout.addWidget(table)

        return layersLayout

    
    
    def on_focus_out(self, event, extra_param):
        num = self.component.gridWidgets[f'{extra_param}Number'].text()
        self.update_table_rows(self.component.gridWidgets[f'{extra_param}Table'], num)
    
    def on_text_changed(self, flag):
        
        if flag == "bottomRepairPly" and self.component.gridWidgets['topPlyNumber'].text() and self.component.gridWidgets['topRepairPlyNumber'].text():
            try:
                topPlyNumber = float(self.component.gridWidgets['topPlyNumber'].text())
                topRepairPlyNumber = float(self.component.gridWidgets['topRepairPlyNumber'].text())
                if topRepairPlyNumber < topPlyNumber:
                    # print("上铺层修补件层数不可小于上铺层层数！")
                    return
            except ValueError:
                pass
        
        
        num = self.component.gridWidgets[f'{flag}Number'].text()
        self.update_table_rows(self.component.gridWidgets[f'{flag}Table'], num)

    def update_table_rows(self, table, num):
        try:
            num_rows = int(num)
            table.setRowCount(num_rows)
            table.set_columns_editable()
        except ValueError:
            table.setRowCount(0)

    def getTableDict(self, table, valueList, descriptionList, type):
        for i in range(table.rowCount()):
            rowDict = {}
            typeList = ["铺层名称", "厚度"]
            for j in range(table.columnCount()):
                
                item = table.item(i, j)
                if type == 1:
                    if j == 1:
                        rowDict[typeList[j]] = toFloat(item.text())
                    else:
                        rowDict[typeList[j]] = item.text()
                elif type == 0:
                    rowDict['厚度'] = toFloat(item.text()) 
            valueList.append(rowDict)
            descriptionList.append(rowDict)
        return valueList
    
    @pyqtSlot()
    def on_pBtnOK_clicked(self):
        
        for flag in ["topPly", "topRepairPly", "bottomPly", "bottomRepairPly"]:
            self.on_text_changed(flag=flag)
        
        valueRes = self.component.getValues()
        descriptionRes = self.component.getValuesUseDescription()
        
        valueRes['topPlyTable'] = []
        valueRes['bottomPlyTable'] = []
        valueRes['topRepairPlyTable'] = []
        valueRes['bottomRepairPlyTable'] = []
    
        descriptionRes['上铺层'] = []
        descriptionRes['下铺层'] = []
        descriptionRes['上铺层修补层'] = []
        descriptionRes['下铺层修补层'] = []
        
        self.getTableDict(self.component.gridWidgets['topPlyTable'], valueRes['topPlyTable'], descriptionRes['上铺层'], 0)
        self.getTableDict(self.component.gridWidgets['bottomPlyTable'], valueRes['bottomPlyTable'],descriptionRes['下铺层'], 0)
        self.getTableDict(self.component.gridWidgets['topRepairPlyTable'], valueRes['topRepairPlyTable'],descriptionRes['上铺层修补层'], 1)
        self.getTableDict(self.component.gridWidgets['bottomRepairPlyTable'], valueRes['bottomRepairPlyTable'],descriptionRes['下铺层修补层'], 1)
        
        if (self.component.gridWidgets['damageType'].currentText() != "贯穿式损伤"):
            descriptionRes.pop('额外挖除尺寸', None)
            descriptionRes.pop('下铺层修补胶层层数', None)
            descriptionRes.pop('下铺层修补层层数', None)
            descriptionRes.pop('下铺层修补层', None)
            valueRes.pop('additionalExcisionSize', None)
            valueRes.pop('lowerAdhesiveLayersCount', None)
            valueRes.pop('bottomRepairPlyNumber', None)
            valueRes.pop('bottomRepairPlyTable', None)
        
        if valueRes['topPlyNumber'] and valueRes['topRepairPlyNumber']:
            try:
                topPlyNumber = float(valueRes['topPlyNumber'])
                topRepairPlyNumber = float(valueRes['topRepairPlyNumber'])
                if topRepairPlyNumber < topPlyNumber:
                   return envs.MBox.warning("上铺层修补件层数不可小于上铺层层数！")
            except ValueError:
                pass
                # return envs.MBox.warning("上铺层修补件层数不可小于上铺层层数！")
            
        if not self.check_empty_values(descriptionRes['上铺层']):
            return envs.MBox.warning("请补全上铺层数据")
        if not self.check_empty_values(descriptionRes['下铺层']):
            return envs.MBox.warning("请补全下铺层数据")
        if not self.check_empty_values(descriptionRes['上铺层修补层']):
            return envs.MBox.warning("请补全上铺层修补层数据")
        if (self.component.gridWidgets['damageType'].currentText() == "贯穿式损伤") and not self.check_empty_values(descriptionRes['下铺层修补层']) :
            return envs.MBox.warning("请补全下铺层修补层数据")
        
        file = "data/ipimas/uis/ParamsModeling.json"
        Utils.write_json(file, valueRes)
        if hasattr(envs, 'ipimas_updateMaterialSetting'):
            envs.ipimas_updateMaterialSetting(type="参数化建模")
        self.close()
        self.paramsModeling_save_signal.emit(descriptionRes)
    
    def check_empty_values(self, data_list):
        for item in data_list:
            for key, value in item.items():
                if value == "" or value is None:
                    return False
                #     print(f"属性 '{key}' 的值为空或空字符串")
                # else:
                #     print(f"属性 '{key}' 的值为 '{value}'")    
        return True
    
    def is_table_filled(self, table):
        # 遍历表格中的每个单元格
        for row in range(self.table.rowCount()):
            for column in range(self.table.columnCount()):
                item = self.table.item(row, column)
                # 检查单元格是否为空
                if not item or item.text().strip() == "":
                    return False  # 如果找到空单元格，返回False
        return True  # 所有单元格都已填写
        
    def closeEvent(self, event):
        DlgParamsModeling._shown = False  # 使用类名访问类属性
        DlgParamsModeling._instance = None  # 清除实例引用
        event.accept()
        
def toFloat(value):
    try:
        return float(value)
    except ValueError:
        return None